package com.qlogic.commons.utils.reflect;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import com.qlogic.commons.utils.strings.StringUtils;

public class JarsExplorer implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1548489072227509357L;
	
	private Hashtable packages = new Hashtable ();
	
	private ArrayList packagesList = new ArrayList ();
	
	private ArrayList jarFiles = new ArrayList ();
	
	private URLClassLoader classLoader;
	
	private ClassDecorator decorator = new DefaultClassDecorator ();

	public ClassDecorator getDecorator() {
		return decorator;
	}

	public void setDecorator(ClassDecorator decorator) {
		this.decorator = decorator;
	}

	public void addJarFile (File file) throws IOException {
		jarFiles.add (new JarFileDescriptor (file, new JarFile (file)));
	}
	
	public void proccess () throws MalformedURLException, ClassNotFoundException {
		proccess (true);
	}
	
	public void proccess (boolean sort) throws MalformedURLException, ClassNotFoundException {
		proccess (false, sort);
	}
	
	public void proccess (boolean ignoreReference, boolean sort) throws MalformedURLException, ClassNotFoundException {
		proccess (ignoreReference, sort, null);
	}
	
	public void proccess (boolean ignoreReference, boolean sort, ClassLoader parent) throws MalformedURLException, ClassNotFoundException {
		if (parent == null) {
			parent = Thread.currentThread().getContextClassLoader ();
		}
		int size = jarFiles.size ();
		URL [] urls = new URL [size]; 
		for (int i = 0; i < size; i++) {
			urls [i] = getJarFileDescriptor (i).getFile ().toURL ();
		}
		classLoader = new URLClassLoader (urls, parent);
		
		for (int i = 0; i < getJarFileDescriptorsCount (); i++) {
			JarFileDescriptor jarFileDesc = getJarFileDescriptor (i);
			loadJarFile (classLoader, jarFileDesc.getJarFile (), ignoreReference);
		}
		
		if (sort) {
			sort ();
		}
		
	}
	
	private void loadJarFile (URLClassLoader classLoader, JarFile jarFile, boolean ignoreReference) throws ClassNotFoundException {
		for (Enumeration e = jarFile.entries(); e.hasMoreElements();) {
			JarEntry entry = (JarEntry) e.nextElement();
			loadJarEntry (classLoader, entry, ignoreReference);
		}
	}
	
	private void loadJarEntry (URLClassLoader classLoader, JarEntry entry, boolean ignoreReference) throws ClassNotFoundException {
		if (!entry.isDirectory() && entry.getName().endsWith(".class")
				&& !(entry.getName().indexOf ("$") >= 0)) {
			String longClassName = StringUtils.replace (entry.getName(), "/", ".").substring(0, entry.getName().length() - 6);
			String packageName = longClassName.substring (0, longClassName.lastIndexOf ('.'));
			PackageDescriptor packageDescriptor = getPackage (packageName);
			if (packageDescriptor == null) {
				packageDescriptor = new PackageDescriptor (packageName);
				packageDescriptor.setDecorator (decorator);
				addPackage (packageDescriptor);
			}
		
			try {
				packageDescriptor.addSource (classLoader.loadClass (longClassName));
			} catch (NoClassDefFoundError err) {
				if (!ignoreReference) {
					throw err;
				}
			}
		}
	}
	
	public void sort () {
		Collections.sort (packagesList);
		for (int i = 0; i < getPackagesCount (); i++) {
			PackageDescriptor packageDesc = getPackage (i);
			packageDesc.sortSources ();
		}
	}
	
	public int getPackagesCount () {
		return packagesList.size ();
	}
	
	public PackageDescriptor getPackage (String name) {
		return (PackageDescriptor)packages.get (name);
	}
	
	public PackageDescriptor getPackage (int index) {
		return (PackageDescriptor)packagesList.get (index);
	}
	
	public void addPackage (PackageDescriptor packageDescriptor) {
		packages.put (packageDescriptor.getName (), packageDescriptor);
		packagesList.add (packageDescriptor);
	}
	
	public void removePackage (String name) {
		Object packageDescriptor = packages.get (name);
		if (packageDescriptor != null) {
			packages.remove (name);
			packagesList.remove (packageDescriptor);
		}
	}
	
	public JarFileDescriptor getJarFileDescriptor (int index) {
		return (JarFileDescriptor)jarFiles.get (index);
	}
	
	public int getJarFileDescriptorsCount () {
		return jarFiles.size ();
	}
	
	public static void main (String [] args) throws IOException, ClassNotFoundException {
		String dir = "/softwares/Tomcat5.5/shared/lib/";
		JarsExplorer explorer = new JarsExplorer ();
		explorer.addJarFile (new File (dir + "qlor.jar"));
		explorer.addJarFile (new File (dir + "j2ee4all.jar"));
		explorer.proccess (true, true);
		for (int i = 0; i < explorer.getPackagesCount(); i++) {
			PackageDescriptor packageDesc = explorer.getPackage (i);
			System.out.println (packageDesc.getName ());
			for (int j = 0; j < packageDesc.getSourcesCount (); j++) {
				System.out.println ("\t" + packageDesc.getSource (j).getName ());
			}
		}
	}

	public URLClassLoader getClassLoader() {
		return classLoader;
	}
	
}
